/*日志器的实现*/

#ifndef __M_LOGGER__
#define __M_LOGGER__
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include "level.hpp"
#include "format.hpp"
#include "message.hpp"
#include "Sink.hpp"
#include "util.hpp"
#include "buffer.hpp"
#include "looper.hpp"
#include <stdarg.h>
#include <atomic>
#include <thread>
#include <mutex>
#include <unordered_map>

namespace Alpaca
{
    class Logger
    {
    public:
        using ptr = std::shared_ptr<Logger>;

        Logger(const std::string &logger_name, LogLevel::value limit_level,
               Formatter::ptr &formatter, std::vector<LogSink::ptr> &sinks)
            : _logger_name(logger_name), _limit_level(limit_level),
              _formatter(formatter), _sinks(sinks.begin(), sinks.end()) {}

        // 通过传入的参数构建一个msg对象，且调用目标方法所生成的日志等级一定与该方法一致
        void debug(const std::string &file, size_t line, const std::string &fmt, ...)
        {
            // 判断输出等级
            if (LogLevel::value::DEBUG < _limit_level)
                return;

            // 解析不定参数
            va_list ap;
            va_start(ap, fmt);
            char *res;
            int ret = vasprintf(&res, fmt.c_str(), ap);
            if (ret == -1)
            {
                std::cout << "vasprintf failed" << std::endl;
                return;
            }
            va_end(ap);

            serialize(LogLevel::value::DEBUG, file, line, res);
            free(res); // vsprintf内部会动态开辟空间给res
        }

        void info(const std::string &file, size_t line, const std::string &fmt, ...)
        {
            // 判断输出等级
            if (LogLevel::value::INFO < _limit_level)
                return;

            // 解析不定参数
            va_list ap;
            va_start(ap, fmt);
            char *res;
            int ret = vasprintf(&res, fmt.c_str(), ap);
            if (ret == -1)
            {
                std::cout << "vasprintf failed" << std::endl;
                return;
            }
            va_end(ap);

            serialize(LogLevel::value::INFO, file, line, res);
            free(res); // vsprintf内部会动态开辟空间给res
        }

        void warn(const std::string &file, size_t line, const std::string &fmt, ...)
        {
            // 判断输出等级
            if (LogLevel::value::WARN < _limit_level)
                return;

            // 解析不定参数
            va_list ap;
            va_start(ap, fmt);
            char *res;
            int ret = vasprintf(&res, fmt.c_str(), ap);
            if (ret == -1)
            {
                std::cout << "vasprintf failed" << std::endl;
                return;
            }
            va_end(ap);

            serialize(LogLevel::value::WARN, file, line, res);
            free(res); // vsprintf内部会动态开辟空间给res
        }

        void error(const std::string &file, size_t line, const std::string &fmt, ...)
        {
            // 判断输出等级
            if (LogLevel::value::ERROR < _limit_level)
                return;

            // 解析不定参数
            va_list ap;
            va_start(ap, fmt);
            char *res;
            int ret = vasprintf(&res, fmt.c_str(), ap);
            if (ret == -1)
            {
                std::cout << "vasprintf failed" << std::endl;
                return;
            }
            va_end(ap);

            serialize(LogLevel::value::ERROR, file, line, res);
            free(res); // vsprintf内部会动态开辟空间给res
        }

        void fatal(const std::string &file, size_t line, const std::string &fmt, ...)
        {
            // 判断输出等级
            if (LogLevel::value::FATAL < _limit_level)
                return;

            // 解析不定参数
            va_list ap;
            va_start(ap, fmt);
            char *res;
            int ret = vasprintf(&res, fmt.c_str(), ap);
            if (ret == -1)
            {
                std::cout << "vsprintf failed" << std::endl;
                return;
            }
            va_end(ap);

            serialize(LogLevel::value::FATAL, file, line, res);
            free(res); // vsprintf内部会动态开辟空间给res
        }

        std::string GetLoggerName() // 不返回引用防止外界修改
        {
            return _logger_name;
        }

    protected:
        virtual void log(const char *data, size_t len) = 0;

        void serialize(LogLevel::value level, const std::string &file, size_t line, char *str)
        {
            // 构建Logmsg对象
            LogMsg msg(level, file, line, _logger_name, str);

            // 获取格式化后的字符串
            std::stringstream ss;
            _formatter->format(ss, msg);

            // 日志落地
            log(ss.str().c_str(), ss.str().size());
        }

    protected:
        std::mutex _mutex;
        std::string _logger_name;
        std::atomic<LogLevel::value> _limit_level;
        Formatter::ptr _formatter;
        std::vector<LogSink::ptr> _sinks;
    };

    class SyncLogger : public Logger
    {
    public:
        SyncLogger(const std::string &logger_name, LogLevel::value limit_level,
                   Formatter::ptr &formatter, std::vector<LogSink::ptr> &sinks)
            : Logger(logger_name, limit_level, formatter, sinks) {}

    protected:
        // 直接通过落地模块的句柄进行日志落地
        void log(const char *data, size_t len)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            if (_sinks.empty())
                return;
            for (auto sink : _sinks)
            {
                sink->log(data, len);
            }
        }
    };

    enum LoggerType
    {
        LOGGER_SYNC,
        LOGGER_ASYNC
    };

    class LoggerBuilder
    {
    public:
        LoggerBuilder()
            : _logger_type(LoggerType::LOGGER_SYNC),
              _limit_level(LogLevel::value::DEBUG),
              _looper_type(AsyncType::ASYNC_SAFE) {}

        void BuildLoggerType(LoggerType type)
        {
            _logger_type = type;
        }

        void BuildLoggerName(std::string name)
        {
            _logger_name = name;
        }

        void BuildLoggerLevel(LogLevel::value level)
        {
            _limit_level = level;
        }

        void BuildEnableUsafeAsync()
        {
            _looper_type = AsyncType::ASYNC_UNSAFE;
        }

        void BuildFormatter(const std::string &pattern)

        {
            _formatter = std::make_shared<Formatter>(pattern);
        }

        template <class SinkType, class... Args>
        void BuildSinks(Args &&...args)
        {
            LogSink::ptr psink = SinkFactory::create<SinkType>(std::forward<Args>(args)...);
            _sinks.push_back(psink);
        }

        virtual Logger::ptr build() = 0;

    protected:
        AsyncType _looper_type;
        LoggerType _logger_type;
        std::string _logger_name;
        std::atomic<LogLevel::value> _limit_level;
        Formatter::ptr _formatter;
        std::vector<LogSink::ptr> _sinks;
    };

    class AsyncLogger : public Logger
    {
    public:
        AsyncLogger(const std::string &logger_name, LogLevel::value limit_level,
                    Formatter::ptr &formatter, std::vector<LogSink::ptr> &sinks,
                    AsyncType logger_type)
            : Logger(logger_name, limit_level, formatter, sinks),
              _looper(std::make_shared<AsyncLooper>(std::bind(&AsyncLogger::realLog, this, std::placeholders::_1), logger_type)) {}

        void log(const char *data, size_t len)
        {
            _looper->push(data, len);
        }

        void realLog(Buffer &buf)
        {
            if (_sinks.empty())
                return;
            for (auto sink : _sinks)
            {
                sink->log(buf.begin(), buf.ReadAbleSize());
            }
        }

    private:
        AsyncLooper::ptr _looper;
    };

    class LocalLoggerBuilder : public LoggerBuilder
    {
    public:
        Logger::ptr build() override
        {
            assert(_logger_name.empty() == false);
            if (_formatter.get() == nullptr)
            {
                _formatter = std::make_shared<Formatter>();
            }
            if (_sinks.empty())
            {
                BuildSinks<StdoutSink>();
            }
            if (_logger_type == LOGGER_ASYNC)
            {
                return std::make_shared<AsyncLogger>(_logger_name, _limit_level, _formatter, _sinks, _looper_type);
            }

            return std::make_shared<SyncLogger>(_logger_name, _limit_level, _formatter, _sinks);
        }
    };

    class LoggerManager
    {
    public:
        static LoggerManager &getInstance()
        {
            static LoggerManager eton;
            return eton;
        }

        void AddLogger(Logger::ptr &logger)
        {
            if (IsLoggerExist(logger->GetLoggerName()))
            {
                return;
            }
            std::unique_lock<std::mutex> lock(_mutex);
            _loggers[logger->GetLoggerName()] = logger;
        }

        bool IsLoggerExist(const std::string &name)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto pos = _loggers.find(name);
            if (pos == _loggers.end())
                return false;
            return true;
        }

        Logger::ptr GetLogger(const std::string &name)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto pos = _loggers.find(name);
            if (pos == _loggers.end())
            {
                return Logger::ptr();
            }
            return pos->second;
        }

        Logger::ptr rootLogger()
        {
            return _root_logger;
        }

    private:
        LoggerManager()
        {
            std::unique_ptr<LoggerBuilder> builder(new LocalLoggerBuilder());
            builder->BuildLoggerName("root");
            _root_logger = builder->build();
            _loggers["root"] = _root_logger;
        }

    private:
        std::mutex _mutex;
        Logger::ptr _root_logger;
        std::unordered_map<std::string, Logger::ptr> _loggers;
    };

    class GlobalLoggerBuilder : public LoggerBuilder
    {
    public:
        Logger::ptr build() override
        {
            assert(_logger_name.empty() == false);
            if (_formatter.get() == nullptr)
            {
                _formatter = std::make_shared<Formatter>();
            }
            if (_sinks.empty())
            {
                BuildSinks<StdoutSink>();
            }
            Logger::ptr logger;
            if (_logger_type == LOGGER_ASYNC)
                logger = std::make_shared<AsyncLogger>(_logger_name, _limit_level, _formatter, _sinks, _looper_type);
            else
                logger = std::make_shared<SyncLogger>(_logger_name, _limit_level, _formatter, _sinks);

            LoggerManager::getInstance().AddLogger(logger);
            return logger;
        }
    };
}

#endif